package com.nebrija.riif.model;

import java.util.Calendar;

public class StepTimelineExecution implements TimelineExecution {

	private Timeline timeline;
	
	private State curState;
	
	// Any time could be used as a reference. We will be adding steps.
	private Calendar referenceTime;
	private Calendar stateReferenceTime;
	private Calendar changeStateTime;
	
	private Calendar curTime;

	private boolean running;
	
	public StepTimelineExecution(Timeline timeline) {
		this.timeline = timeline;
		this.referenceTime = Calendar.getInstance();
		this.stateReferenceTime = (Calendar) referenceTime.clone();
	}

	@Override
	public Double getTimeSinceLastTransition(TimeStrings ts) {
		if(stateReferenceTime==null || curTime==null){
			return null;
		}
		int field = TimeStrings.timeStringsToCalendar(ts);
		long msdiff = curTime.getTimeInMillis() - stateReferenceTime.getTimeInMillis();
		double finalVal = 0;
		switch(field){
			case Calendar.MONTH: 
								finalVal = curTime.get(Calendar.MONTH) - stateReferenceTime.get(Calendar.MONTH);
								finalVal+= 12*(curTime.get(Calendar.YEAR) - stateReferenceTime.get(Calendar.YEAR));
								break;
			case Calendar.YEAR: 
								finalVal= curTime.get(Calendar.YEAR) - stateReferenceTime.get(Calendar.YEAR);
								break;
			case Calendar.DATE:
								finalVal = (double)msdiff/(1000*60*60*24);
								break;
			case Calendar.HOUR:
								finalVal = (double)msdiff/(1000*60*60);
								break;
			case Calendar.MINUTE:
								finalVal = (double)msdiff/(1000*60);
								break;
			case Calendar.SECOND:
								finalVal = (double)msdiff/1000;
								break;
			default:
								finalVal = msdiff;
		}
		return finalVal;
	}

	@Override
	public State getCurrentState() {
		return curState;
	}

	@Override
	public void runTimeline() {
		if(running){
			System.out.println("Error. The timeline is already running");
		}
		running = true;
		curState = timeline.getInitialState();
		ValueTimeUnit vtu = curState.getDuration();
		referenceTime = Calendar.getInstance();
		stateReferenceTime = (Calendar) referenceTime.clone();
		changeStateTime = (Calendar) stateReferenceTime.clone();
		curTime = (Calendar) stateReferenceTime.clone();
		int field = TimeStrings.timeStringsToCalendar(vtu.getTimeUnit());
		changeStateTime.add(field, (int) vtu.getValue());
	}

	@Override
	public void stop (){
		running = false;
	}
	
	public String step (ValueTimeUnit vtu) {
		if(!running){
			return("Error. The timeline is not running");
		}
		curTime.add(TimeStrings.timeStringsToCalendar(vtu.getTimeUnit()), (int)vtu.getValue());
		
		while (curTime.compareTo(changeStateTime)>=0){
			//Transition to next state
			String stName = curState.getNextStateName();
			if(stName==null){
				running = false;
				return("No more transitions available from state " +curState.getName()+ " in timeline " + timeline.getName());
			}
			System.out.println("Performing transition to state " +stName+ " in timeline " + timeline.getName());
			curState = (State) timeline.getElement(stName);
			stateReferenceTime = (Calendar) changeStateTime.clone();
			ValueTimeUnit v2 = curState.getDuration();
			if(v2 == null){
				running = false;
				return("No more transitions available from state " +curState.getName()+ " in timeline " + timeline.getName());			
			}
			int field = TimeStrings.timeStringsToCalendar(v2.getTimeUnit());
			// Time to the next transition
			changeStateTime.add(field, (int) v2.getValue());
		}
		return ("Step completed");
	}

	@Override
	public Double getTimeSince(Calendar cal, TimeStrings ts) {
		if(curTime==null){
			return null;
		}
		int field = TimeStrings.timeStringsToCalendar(ts);
		long msdiff = curTime.getTimeInMillis() - cal.getTimeInMillis();
		double finalVal = 0;
		switch(field){
			case Calendar.MONTH: 
								finalVal = curTime.get(Calendar.MONTH) - cal.get(Calendar.MONTH);
								finalVal+= 12*(curTime.get(Calendar.YEAR) - cal.get(Calendar.YEAR));
								break;
			case Calendar.YEAR: 
								finalVal= curTime.get(Calendar.YEAR) - cal.get(Calendar.YEAR);
								break;
			case Calendar.DATE:
								finalVal = (double)msdiff/(1000*60*60*24);
								break;
			case Calendar.HOUR:
								finalVal = (double)msdiff/(1000*60*60);
								break;
			case Calendar.MINUTE:
								finalVal = (double)msdiff/(1000*60);
								break;
			case Calendar.SECOND:
								finalVal = (double)msdiff/1000;
								break;
			default:
								finalVal = msdiff;
		}
		return finalVal;
	}

	public String forceTransition() {
		String stName = curState.getNextStateName();
		if(stName==null){
			running = false;
			return("No more transitions available from state " +curState.getName()+ " in timeline " + timeline.getName());
		}
		System.out.println("Performing transition to state " +stName+ " in timeline " + timeline.getName());
		curState = (State) timeline.getElement(stName);
		stateReferenceTime = (Calendar) curTime.clone(); //instead of the change time as we have forced the transition
		ValueTimeUnit v2 = curState.getDuration();
		if(v2 == null){
			running = false;
			return("No more transitions available from state " +curState.getName()+ " in timeline " + timeline.getName());			
		}
		int field = TimeStrings.timeStringsToCalendar(v2.getTimeUnit());
		// Time to the next transition
		changeStateTime.add(field, (int) v2.getValue());
		return ("Transition completed");
	}
	
}
